﻿namespace Lr.Core.Numbers
{
    /// <summary>
    /// Defines an <see cref="Int32"/> based <see cref="IRangeCycler"/>.
    /// Cycles through a given <see cref="IRange"/> of <see cref="int"/> from minimulm to maximum. When the maximum is reached, the internal counter will reset itself to the range's minimum.
    /// </summary>
    public sealed class Int32RangeCycler : IRangeCycler<int>
    {
        #region Construction
        /// <summary>
        /// Creates a new <see cref="Int32RangeCycler"/> object.
        /// </summary>
        /// <param name="range">The <see cref="IRange"/> of <see cref="Int32"/> the <see cref="ICounter"/> operates in.</param>
        public Int32RangeCycler(IRange<int> range)
        {
            ThrowIf.Null(() => range);

            _range = range;

            Reset();
        }
        #endregion

        #region IRangeCycler
        /// <summary>
        /// Gets the <see cref="IRange"/> of <typeparamref name="T"/> this counter operates in.
        /// </summary>
        public IRange<int> Range { get { return _range; } }
        private readonly IRange<int> _range;

        /// <summary>
        /// Gets the current value in the counter.
        /// </summary>
        public int Current { get { return _currentValue; } }
        private int _currentValue;

        /// <summary>
        /// Gets the next value in the counter.
        /// </summary>
        /// <returns>The next <typeparamref name="T"/> value.</returns>
        public int Next()
        {
            if (_currentValue > this.Range.Maximum)
                Reset();

            return _currentValue++;
        }

        /// <summary>
        /// Gets the previous value in the counter.
        /// </summary>
        /// <returns>The next <typeparamref name="T"/> value.</returns>
        public int Previous()
        {
            if (_currentValue < this.Range.Minimum)
                Reset(this.Range.Maximum);

            return _currentValue--;
        }

        /// <summary>
        /// Peeks what the next value in the counter will be.
        /// </summary>
        /// <returns>The next <typeparamref name="T"/> value.</returns>
        public int Peek()
        {
            var currentValueSimulation = _currentValue;

            if (currentValueSimulation > this.Range.Maximum)
                currentValueSimulation = this.Range.Minimum;

            return currentValueSimulation++;
        }

        /// <summary>
        /// Resets the <see cref="ICounter"/>.
        /// </summary>
        public void Reset()
        {
            Reset(this.Range.Minimum);
        }

        /// <summary>
        /// Resets the <see cref="ICounter"/> with the given <paramref name="initialValue"/>.
        /// </summary>
        /// <param name="initialValue">The value to resert the <see cref="ICounter"/> to.</param>
        public void Reset(int initialValue)
        {
            if (this.Range.IsInRange(initialValue))
                _currentValue = initialValue;
            else
                Reset();
        }
        #endregion

        /// <summary>
        /// Returns a <see cref="String"/> that represents the object.
        /// </summary>
        /// <returns>A <see cref="String"/> that represents the object.</returns>
        public override string ToString()
        {
            return this.ToDebugString();
        }
    }
}
